﻿using System;
using System.Text;
using System.Collections.Generic;
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Lexer;
using System.Text.RegularExpressions;

namespace Lexer_Tests {
	/// <summary>
	/// Summary description for UnitTest1
	/// </summary>
	[TestClass]
	public class LexerTests {
		private TestContext testContextInstance;

		/// <summary>
		///Gets or sets the test context which provides
		///information about and functionality for the current test run.
		///</summary>
		public TestContext TestContext {
			get {
				return testContextInstance;
			}
			set {
				testContextInstance = value;
			}
		}

		Lexer<string> l = new Lexer<string>();

		private void defaultAmbig() {
			l.AmbiguousCallback = (pos, lexeme) => "[ambig@" + pos + ":" + lexeme + "]";
		}

		private void defaultUnrec() {
			l.UnrecognizedCallback = (pos, lexeme) => "[unrec@" + pos + ":" + lexeme + "]";
		}

		private void reg(params object[] objs) {
			l.RegisterPattern(new LexerPattern(objs), (pos, lexeme) => "[" + pos + ":" + lexeme + "]");
		}

		private void scansTo(string s, string expected) {
			Assert.AreEqual(expected, scanAndConcat(s));
		}

		private string scanAndConcat(string s) {
			StringBuilder result = new StringBuilder();
			foreach (string t in l.Scan(s)) {
				result.Append(t);
			}
			return result.ToString();
		}

		[TestMethod]
		public void SingleCharacter() {
			reg("a");
			scansTo("a", "[0:a]");
		}

		[TestMethod]
		public void CharacterSequence1() {
			reg("a");
			scansTo("aa", "[0:a][1:a]");
		}

		[TestMethod]
		public void CharacterSequence2() {
			reg("a");
			reg("b");
			reg("c");
			scansTo("baca", "[0:b][1:a][2:c][3:a]");
		}

		[TestMethod]
		public void Regex1() {
			reg(new Regex("\\d"));
			scansTo("123", "[0:1][1:2][2:3]");
		}

		[TestMethod]
		public void Regex2() {
			reg(new Regex("a|b"));
			scansTo("abba", "[0:a][1:b][2:b][3:a]");
		}

		[TestMethod]
		[ExpectedException(typeof(InvalidOperationException))]
		public void AmbigRules() {
			reg("a");
			reg("a");
		}

		[TestMethod]
		public void Ambig1() {
			defaultAmbig();
			reg("a");
			reg(new Regex("a|b"));
			scansTo("a", "[ambig@0:a]");
		}

		[TestMethod]
		public void Ambig2() {
			defaultAmbig();
			reg(new Regex("a|b"));
			reg(new Regex("b|c"));
			scansTo("abc", "[0:a][ambig@1:b][2:c]");
		}

		[TestMethod]
		public void Unrec1() {
			defaultUnrec();
			scansTo("a", "[unrec@0:a]");
		}

		[TestMethod]
		public void Unrec2() {
			defaultUnrec();
			reg("b");
			scansTo("abc", "[unrec@0:a][1:b][unrec@2:c]");
		}

		[TestMethod]
		public void Unrec3() {
			defaultUnrec();
			scansTo("abc", "[unrec@0:a][unrec@1:b][unrec@2:c]");
		}

		[TestMethod]
		public void Word() {
			reg("abc");
			scansTo("abc", "[0:abc]");
		}

		[TestMethod]
		public void TwoDistinctWords() {
			reg("abc");
			reg("def");
			scansTo("abcdef", "[0:abc][3:def]");
		}

		[TestMethod]
		public void TwoSharedWords1() {
			reg("abc");
			reg("abd");
			scansTo("abc", "[0:abc]");
		}

		[TestMethod]
		public void TwoSharedWords2() {
			reg("abc");
			reg("abd");
			scansTo("abcabd", "[0:abc][3:abd]");
		}

		[TestMethod]
		public void Loop() {
			reg("a+");
			scansTo("aaa", "[0:aaa]");
		}

		[TestMethod]
		public void MixedRegex1() {
			reg(new Regex("a|b"), "+");
			scansTo("abab", "[0:abab]");
		}

		[TestMethod]
		public void MixedRegex2() {
			defaultUnrec();
			reg("!", new Regex("a|c"));
			scansTo("abc", "[unrec@0:a][1:b][unrec@2:c]");
		}

		[TestMethod]
		public void MixedRegex3() {
			defaultUnrec();
			reg("!", new Regex("b|c"), "+");
			scansTo("abcdabcd", "[0:a][unrec@1:b][unrec@2:c][3:da][unrec@5:b][unrec@6:c][7:d]");
		}

		[TestMethod]
		public void Escape1() {
			reg("\\a");
			scansTo("a", "[0:a]");
		}

		[TestMethod]
		public void Escape2() {
			reg("\\+");
			scansTo("+", "[0:+]");
		}

		[TestMethod]
		public void Eof1() {
			l.EofCallback = (pos, lexeme) => "[" + pos + ":" + "eof]";
			scansTo("", "[0:eof]");
		}

		[TestMethod]
		public void Eof2() {
			l.EofCallback = (pos, lexeme) => "[" + pos + ":" + "eof]";
			reg("a");
			scansTo("a", "[0:a][1:eof]");
		}

		[TestMethod]
		public void RegexShared1() {
			reg(new Regex("a|b"), "c");
			reg("ad");
			scansTo("acad", "[0:ac][2:ad]");
		}
	}
}
